package com.zt.questionnaire.common.exception;

import com.zt.questionnaire.common.msg.HErrorCode;
import com.zt.questionnaire.common.msg.HResultMsg;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 通用的全局异常处理类
 */
@Slf4j
@RestControllerAdvice
public class HExceptionHandler {

    /**
     * 处理调用接口校验失败抛出的异常
     * 参数异常拦截(Get请求 ？拼接形式)
     *
     * @param e
     * @return
     */
    @ExceptionHandler(value = BindException.class)
    public String bindExceptionHandler(BindException e) {
        log.error("=============================进入全局异常处理=============================");
        List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
        List<String> msgList = fieldErrors.stream()
            .map(FieldError::getDefaultMessage)
            .collect(Collectors.toList());
        // 一次只会抛出一个错误
        String msg = msgList.get(0);
        log.error(e.getMessage(), msg);
        return HResultMsg.createMsgError(HErrorCode.PARAM_ERROR, msg);
    }

    /**
     * 参数异常拦截
     *
     * @param e
     * @return
     */
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public String methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
        log.error("=============================进入全局异常处理=============================");
        List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
        List<String> msgList = fieldErrors.stream()
            .map(FieldError::getDefaultMessage)
            .collect(Collectors.toList());
        // 一次只会抛出一个错误
        String msg = msgList.get(0);
        log.error(e.getMessage(), msg);
        return HResultMsg.createMsgError(HErrorCode.PARAM_ERROR, msg);
    }

    /**
     * 参数异常拦截 （方法上的）
     *
     * @param e
     * @return
     * @date 2020/11/17 0017
     **/
    @ExceptionHandler(ConstraintViolationException.class)
    public String constraintViolationException(ConstraintViolationException e, HttpServletRequest request) {
        log.error("=============================进入全局异常处理=============================");
        StringBuilder msg = new StringBuilder();
        Set<ConstraintViolation<?>> constraintViolations = e.getConstraintViolations();
        for (ConstraintViolation<?> constraintViolation : constraintViolations) {
            String message = constraintViolation.getMessage();
            msg.append(message);
        }
        log.error(e.getMessage(), msg);
        return HResultMsg.createMsgError(HErrorCode.PARAM_ERROR, msg.toString());
    }

    /**
     * 主键冲突异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(value = DuplicateKeyException.class)
    public String duplicateKeyExceptionHandler(DuplicateKeyException e) {
        log.error("=============================进入全局异常处理=============================");
        log.error(e.getMessage(), e);
        return HResultMsg.createMsgError(HErrorCode.PARAM_ERROR, "主键冲突");
    }

    /**
     * 请求方式错误异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
    public String httpRequestMethodNotSupportedExceptionHandler(HttpRequestMethodNotSupportedException e, HttpServletRequest request) {
        log.error("=============================进入全局异常处理=============================");
        String method = request.getMethod();
        String msg = method + "请求方式错误";
        log.error(e.getMessage(), e);
        return HResultMsg.createMsgError(HErrorCode.PARAM_ERROR, msg);
    }

    /**
     * 处理自定义异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(value = CustomException.class)
    public String customExceptionHandler(CustomException e) {
        log.error("=============================进入全局异常处理=============================");
        String errorMsg = e.getMessage();
        int errorCode = HErrorCode.SERVER_EXCEPTION;
        log.error(errorMsg);
        int index = errorMsg.lastIndexOf(":");
        if (index != -1) {
            try {
                errorCode = Integer.parseInt(errorMsg.substring(index + 1));
                errorMsg = errorMsg.substring(0, index);
            } catch (Exception e2) {
                log.error(e.getMessage(), e2);
            }
        }
        return HResultMsg.createMsgError(errorCode, errorMsg);
    }

    /**
     * 处理系统级异常Exception
     *
     * @param e
     * @return
     */
    @ExceptionHandler(value = Exception.class)
    public String defaultErrorHandler(Exception e) {
        log.error("=============================进入全局异常处理=============================");
        int errorCode = HErrorCode.SERVER_EXCEPTION;
        String errorMsg = "服务器异常";

        log.error(e.getMessage(), e);
        /*
         * 返回json数据或者String数据：
         * 那么需要在方法上加上注解：@ResponseBody
         * 添加return即可。
         */
        return HResultMsg.createMsgError(errorCode, errorMsg);

        /*
         * 返回视图：
         * 定义一个ModelAndView即可，
         * 然后return;
         * 定义视图文件(比如：error.html,error.ftl,error.jsp);
         *
         */
    }

}
